package memorphic.slide
{
	
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.events.Event;
	
	import mx.core.Application;
	import mx.core.LayoutContainer;
	import mx.core.UIComponent;
	
	/**
	 * SlideView is intended to be a base class for views. Extend it to get:
	 *    <ul>
	 * 		<li>View state management, via <code>stateManager</code> and Flex states.</li>
	 * 		<li><code>[Cascade]</code> metadata</li>
	 * 	  </ul>
	 * 
	 */
	public class SlideView extends LayoutContainer implements IStateView //, IModifiableSlideView
	{
		
		
		private var __dirtyParent:Boolean = true;
		private var __parentView:IStateView;
		private var __explicitParentView:IStateView;
		
		private var __stateView:ISlideStateClient;
		
		private var slideDelegate:SlideDelegate;
		
		
	
		public function SlideView()
		{
			slideDelegate = new SlideDelegate(this);
			addEventListener(Event.ADDED_TO_STAGE, onAddedToStage, false, int.MAX_VALUE);
			addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage, false, int.MAX_VALUE);
		}
		
		
		/**
		 * Override in subclasses
		 */
		protected function setUpView():void
		{
			
		}


		public function get currentViewState():ViewState
		{
			var states:Array = slideStates;
			var n:int = states.length;
			for(var i:int=0; i<n ;i++){
				if(ViewState(states[i]).name == currentState){
					return states[i] as ViewState;
				}
			}
			return null;
		}

		
		public function get stateView():*
		{
			return __stateView || this;
		}
		public function set stateView(view:*):void
		{
			if(view is ISlideStateClient){
				__stateView = view;
			}else if(view is UIComponent){
				__stateView = new UIComponentStateAdapter(view as UIComponent);
			}else{
				throw new ArgumentError();
			}
		}
		
		
		[Bindable]
		public var defaultState:String = "";
		
		
		[Cascade]
		[Bindable]
		public var stateManager:StateManager;

		
		public function get slideStates():Array
		{
			var slideStates:Array = new Array();
			var n:int = states.length;
			for(var i:int=0; i<n; i++){
				if(states[i] is ViewState){
					slideStates.push(states[i]);
				}
			}
			return slideStates;
		}
		
		
		
		[Bindable("addedToStage")]
		[Bindable("explicitParentSet")]
		public function get parentView():IStateView
		{
			if(__explicitParentView){
				return __explicitParentView;
			}
			if(__dirtyParent){
				if(parent is IStateView){
					__parentView = parent as IStateView;
				}else if(parent is Application){
					__parentView = null;
				}else{
					var p:* = this;
					while(p = p.parent){
						if(p is IStateView){
							return p as IStateView;
						} 
					}
					__parentView = null;
				}
			}
			__dirtyParent = false;
			return __parentView;
		}
		
		public function set parentView(view:IStateView):void
		{
			__dirtyParent = true;
			__explicitParentView = view;
			dispatchEvent(new Event("explicitParentSet"));
		}
		
		
		
		/**
		 * Locates a child View inside a display object, and returns the first one it finds. This is mostly useful
		 * for accessing a composed View inside a container such as a Panel, which cannot extend IStateView itself.
		 * You can pass a more specific type as a second argument, which will only return an instance of that type.
		 * This argument must be a class that implements IStateView.
		 */
		public static function findChildView(parent:DisplayObjectContainer, type:Class=null):IStateView
		{
			if(type == null){
				type = IStateView;
			}
			var numChildren:int = parent.numChildren;
			var child:DisplayObject;
			for(var i:int=0; i<numChildren; i++){
				child = parent.getChildAt(i);
				if(child is type){
					return child as IStateView;
				}else{
					child = findChildView(child as DisplayObjectContainer, type) as DisplayObject;
					if(child is type){
						return child as IStateView;
					}
				}
			}
			return null;
		}
		
		public static function findChildViews(parent:DisplayObjectContainer, type:Class=null):Array
		{
			var views:Array = [];
			if(type == null){
				type = IStateView;
			}
			var numChildren:int = parent.numChildren;
			var child:DisplayObject;
			for(var i:int=0; i<numChildren; i++){
				child = parent.getChildAt(i);
				if(child is type){
					views.push(child);
				}else{
					views = views.concat(findChildViews(child as DisplayObjectContainer, type));
				}
			}
			return views;
		}
		
		
		/**
		 * Locates the nearest parent of the argument object, which is an IStateView. You can pass a more specific
		 * type as a second argument, which will only return an instance of that type. This argument must be a
		 * class that implements IStateView.
		 */
		public static function findParentView(child:DisplayObject, type:Class=null):IStateView
		{
			if(type == null){
				type = IStateView;
			}
			if(child is IStateView){
				if(IStateView(child).parentView is type){
					return child as IStateView;
				}
			}
			var parent:DisplayObjectContainer = child.parent;
			if(parent is type){
				return parent as IStateView;
			}else if(parent is Application){
				return null;
			}else{
				var p:* = child;
				while(p = p.parent){
					if(p is type){
						return p as IStateView;
					} 
				}
				return null;
			}
		}
		
		private function onAddedToStage(e:Event):void
		{
			__dirtyParent = true;
		}
		
		private function onRemovedFromStage(e:Event):void
		{
			__dirtyParent = true;
		}
		
		
		
		[Bindable("addedToStage")]
		public function get viewReady():Boolean
		{
			return root != null;
		}
		


	}

}

